gl renderer: Fix render node texture cache
authorAlexander Larsson <alexl@redhat.com>
Wed, 30 Sep 2020 12:53:13 +0000 (14:53 +0200)
committerAlexander Larsson <alexl@redhat.com>
Wed, 30 Sep 2020 12:53:13 +0000 (14:53 +0200)
We're caching two things, either a node itself being rendered, or a
parent storing a cached version of a child as rendered to an offscreen
the size and location of the parent.

If both the parent and child uses the cache this will cause a conflict in
the cache as it is currently use keying of a node pointer which will have
the same value for the node-as-itself and the child-node-of-the-parent.

We fix this by adding another part to the key "pointer_is_child" which means
we can have the same node pointer twice in the cache.

Additionally, in the child-is-rendered-offscreen case the offscreen
result actually depends on the position and size of the parent viewport,
so we need to store the parent bounds in that case.

gsk/gl/gskgldriver.c
gsk/gl/gskgldriverprivate.h
gsk/gl/gskglrenderer.c

index b1f8d6ea0e5c5b28d4143ececc415abed9098c48..6305add62cc729682cb260e6b18ea4393065a58d 100644 (file)
@@ -587,7 +587,8 @@ texture_key_hash (gconstpointer v)
 
   return GPOINTER_TO_UINT (k->pointer)
          + (guint)(k->scale*100)
-         + (guint)k->filter;
+         + (guint)k->filter * 2 +
+         + (guint)k->pointer_is_child;
 }
 
 static gboolean
@@ -598,7 +599,9 @@ texture_key_equal (gconstpointer v1, gconstpointer v2)
 
   return k1->pointer == k2->pointer &&
          k1->scale == k2->scale &&
-         k1->filter == k2->filter;
+         k1->filter == k2->filter &&
+         k1->pointer_is_child == k2->pointer_is_child &&
+         (!k1->pointer_is_child || graphene_rect_equal (&k1->parent_rect, &k2->parent_rect));
 }
 
 int
index 0bf9ca89d281570a846d5f9fce4ee836b45aeb10..7adf32005753096c33ac78224ae1cb7addc1777f 100644 (file)
@@ -25,6 +25,8 @@ typedef struct {
   gpointer pointer;
   float scale;
   int filter;
+  int pointer_is_child;
+  graphene_rect_t parent_rect; /* Only set if pointer_is_child */
 } GskTextureKey;
 
 GskGLDriver *   gsk_gl_driver_new                       (GdkGLContext    *context);
index 6efe62bca74a63fcf962d6ff842425b54c7de37c..4cfe31535c150615231012bb5b0560b935b62691 100644 (file)
@@ -626,6 +626,7 @@ render_fallback_node (GskGLRenderer   *self,
     return;
 
   key.pointer = node;
+  key.pointer_is_child = FALSE;
   key.scale = scale;
   key.filter = GL_NEAREST;
 
@@ -1922,6 +1923,7 @@ render_blur_node (GskGLRenderer   *self,
     }
 
   key.pointer = node;
+  key.pointer_is_child = FALSE;
   key.scale = ops_get_scale (builder);
   key.filter = GL_NEAREST;
   blurred_region.texture_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
@@ -1982,6 +1984,7 @@ render_inset_shadow_node (GskGLRenderer   *self,
   texture_height = ceilf ((node_outline->bounds.size.height + blur_extra) * scale);
 
   key.pointer = node;
+  key.pointer_is_child = FALSE;
   key.scale = scale;
   key.filter = GL_NEAREST;
   blurred_texture_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);
@@ -3812,7 +3815,6 @@ add_offscreen_ops (GskGLRenderer         *self,
       (flags & FORCE_OFFSCREEN) == 0)
     {
       GdkTexture *texture = gsk_texture_node_get_texture (child_node);
-
       upload_texture (self, texture, texture_region_out);
       *is_offscreen = FALSE;
       return TRUE;
@@ -3825,6 +3827,7 @@ add_offscreen_ops (GskGLRenderer         *self,
 
   /* Check if we've already cached the drawn texture. */
   key.pointer = child_node;
+  key.pointer_is_child = TRUE; /* Don't conflict with the child using the cache too */
   key.scale = ops_get_scale (builder);
   key.filter = filter;
   cached_id = gsk_gl_driver_get_texture_for_key (self->gl_driver, &key);